package com.siyuanren.gcocp.web.wx;

import cn.hutool.core.codec.Base64;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.service.WxPayService;
import com.siyuanren.gcocp.common.config.PublicConfig;
import com.siyuanren.gcocp.common.util.UUIDUitl;
import com.siyuanren.gcocp.consts.GcocpConsts;
import com.siyuanren.gcocp.consts.RedisConsts;
import com.siyuanren.gcocp.entity.WxUser;
import com.siyuanren.gcocp.service.CourseService;
import com.siyuanren.gcocp.service.OrderService;
import com.siyuanren.gcocp.service.WxUserService;
import com.siyuanren.gcocp.web.wx.service.CoreService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * @author guwr
 */
@Slf4j
@RequestMapping("wx/allow")
@Controller
public class WxAllowController extends BaseWxController {


    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private WxPayService payService;
    @Autowired
    private CoreService coreService;
    @Autowired
    private CourseService courseService;
    @Autowired
    private WxUserService userService;
    @Autowired
    private OrderService orderService;


    /**
     * 微信验证 接入指南
     */
    @RequestMapping("core")
    public void core(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        String signature = request.getParameter("signature");
        String nonce = request.getParameter("nonce");
        String timestamp = request.getParameter("timestamp");
        String gen = SHA1.gen(PublicConfig.PUBLIC_WX_TOKEN, timestamp, nonce);
        log.info(",signature=" + signature + ",nonce=" + nonce + ",timestamp=" + timestamp + ",gen=" + gen + ",token=" + PublicConfig.PUBLIC_WX_TOKEN);
        if (!Objects.equals(gen, signature)) {
            // 消息签名不正确，说明不是公众平台发过来的消息
            response.getWriter().println("非法请求");
            log.info("非法请求");
            return;
        }
        String echoStr = request.getParameter("echostr");
        if (StringUtils.isNotBlank(echoStr)) {
            // 说明是一个仅仅用来验证的请求，回显echostr
            String echoStrOut = String.copyValueOf(echoStr.toCharArray());
            response.getWriter().println(echoStrOut);
            return;
        }
        String raw = "raw";
        String encryptType = StringUtils.isBlank(request.getParameter("encrypt_type")) ? raw : request.getParameter("encrypt_type");
        if (raw.equals(encryptType)) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream());
            WxMpXmlOutMessage outMessage = this.coreService.route(inMessage);
            if (outMessage == null) {
                response.getWriter().write("");
            } else {
                response.getWriter().write(outMessage.toXml());
            }
            return;
        }
        response.getWriter().println("不可识别的加密类型");
    }

    /**
     * 获取用户认证授权
     */
    @RequestMapping("authorize")
    public String authorize(String redirectUrl) {
        log.info("auth=" + redirectUrl);
        String redirectURI = PublicConfig.TOMCAT_URL + "/wx/allow/user_info";
        String authorizationUrl = wxMpService.oauth2buildAuthorizationUrl(redirectURI, WxConsts.OAuth2Scope.SNSAPI_USERINFO, redirectUrl);
        log.info("redirect=" + authorizationUrl);
        return String.format("redirect:%s", authorizationUrl);
    }

    @RequestMapping("user_info")
    public String userInfo(HttpServletResponse response, String code, String state) throws Exception {
        WxMpOAuth2AccessToken token = wxMpService.oauth2getAccessToken(code);
        String openid = token.getOpenId();
        WxUser user = userService.getByOpenid(openid);
        if (user == null) {
            //库中不存在
            log.info("库中不存在，先入库，再查询");
            WxMpUser wxMpUser = this.wxMpService.oauth2getUserInfo(token, "zh_CN");
            userService.insertWxMpUser(wxMpUser);
            user = userService.getByOpenid(openid);
        }
        Integer id = user.getId();
        String tokenUUID = UUIDUitl.getUUID();
        Integer expire = GcocpConsts.LOGIN_TOKEN_TIME_OUT;
        jedisFacade.setex(String.format(RedisConsts.TOKEN_PREFIX, tokenUUID), expire, String.valueOf(id));
        // 写入cookie
        Cookie cookie = new Cookie(RedisConsts.TOKEN, tokenUUID);
        cookie.setPath("/");
//        cookie.setMaxAge(expire);
        response.addCookie(cookie);
        return String.format("redirect:%s", Base64.decodeStr(state));
    }

    /**
     * 异步通知
     */
    @ResponseBody
    @RequestMapping("parse_order_notify_result")
    public String parseOrderNotifyResult(HttpServletRequest request) {
        String retStr;
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayOrderNotifyResult result = payService.parseOrderNotifyResult(xmlResult);
            orderService.completeOrder(result);
            //自己处理订单的业务逻辑，需要判断订单是否已经支付过，否则可能会重复调用
            retStr = WxPayNotifyResponse.success("处理成功!");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因{}", e.getMessage());
            retStr = WxPayNotifyResponse.fail(e.getMessage());
        }
        log.info("retStr={}", retStr);
        return retStr;
    }
}
